/**
 * 
 */
package com.ibuonline.search.solr.score;

import java.io.File;

import javax.xml.xpath.XPathConstants;

import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.core.Config;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.Xscorer;
import org.apache.solr.schema.XscorerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;

import com.ibuonline.search.solr.SpringContextHolder;

/**
 * @author Liuye
 * 
 */
public final class ExpressionXscorerFactory extends XscorerFactory {
	private static Logger log = LoggerFactory
			.getLogger(ExpressionXscorerFactory.class);
	private static final String CONFIG_FILE = "config-file";
	// private static final String SCATTER_FIELD = "scatter-field";
	private static final String INNER_EXPRESSION = "inner-expression";
	private static final String OUTER_EXPRESSION = "outer-expression";
	private static final String SCORE_PRIVIDER = "score-provider";
	private String configFile;
	private ScoreExpression innerExpression;// 内部评分表达式对象
	private ScoreExpression outerExpression;// 外部评分表达式对象
	private ScoreProvider scoreProvider;

	// private String scatterFieldName;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.solr.schema.XscorerFactory#init(org.apache.solr.common.params
	 * .SolrParams)
	 */
	@Override
	public void init(SolrConfig solrConfig, IndexSchema schema,
			SolrParams params) {
		super.init(solrConfig, schema, params);
		String configFile = params.get(CONFIG_FILE);
		if (configFile == null) {
			throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
					"ExpressionXscorerFactory must specify argument: '"
							+ CONFIG_FILE + "' -- path to xscore-config.xml");
		}
		File fConfig = new File(solrConfig.getResourceLoader().getConfigDir(),
				configFile);
		if (!fConfig.exists()) {
			throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
					"ExpressionXscorerFactory missing config file: '"
							+ configFile + "\n" + fConfig.getAbsolutePath()
							+ " must exist.");
		}
		this.configFile = configFile;

		loadConfig();
	}

	protected void loadConfig() {
		if (!importLock.tryLock()) {
			log.warn("ExpressionXscorerFactory is running.");
			return;
		}
		statusMessage = "Loading XscorerFactory's configuration...";
		log.info("Loading ExpressionXscorerFactory's configuration from: "
				+ solrConfig.getResourceLoader().getConfigDir() + configFile);
		try {
			Config cfg = new Config(solrConfig.getResourceLoader(), configFile);
			// 获取打散字段配置项
			// Node scatterFieldNode = (Node) cfg.evaluate("config/"
			// + SCATTER_FIELD, XPathConstants.NODE);
			// if (scatterFieldNode != null) {
			// String scatterFieldName = scatterFieldNode.getTextContent();
			// this.scatterFieldName = scatterFieldName.replaceAll("\t|\r|\n",
			// "");// 去除字符串中的回车/换行符/制表符
			// }

			// 获取内部表达式配置项
			Node innerExpressionNode = (Node) cfg.evaluate("config/"
					+ INNER_EXPRESSION, XPathConstants.NODE);
			if (innerExpressionNode == null) {
				throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
						"ExpressionXscorerFactory config file must has a element: '"
								+ INNER_EXPRESSION + "'");
			}
			String innerExpressionString = innerExpressionNode.getTextContent();
			if (innerExpressionString == null) {
				throw new SolrException(
						SolrException.ErrorCode.SERVER_ERROR,
						"the element '"
								+ INNER_EXPRESSION
								+ "' in ExpressionXscorerFactory config file can't be null");
			}
			innerExpressionString = innerExpressionString.replaceAll(
					"\t|\r|\n", "");// 去除字符串中的回车/换行符/制表符
			innerExpression = new ScoreExpression(innerExpressionString);

			// 获取外部表达式配置项
			Node outerExpressionNode = (Node) cfg.evaluate("config/"
					+ OUTER_EXPRESSION, XPathConstants.NODE);
			if (outerExpressionNode == null) {
				throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
						"ExpressionXscorerFactory config file must has a element: '"
								+ OUTER_EXPRESSION + "'");
			}
			String outerExpressionString = outerExpressionNode.getTextContent();
			if (outerExpressionString == null) {
				throw new SolrException(
						SolrException.ErrorCode.SERVER_ERROR,
						"the element '"
								+ OUTER_EXPRESSION
								+ "' in ExpressionXscorerFactory config file can't be null");
			}
			outerExpressionString = outerExpressionString.replaceAll(
					"\t|\r|\n", "");// 去除字符串中的回车/换行符/制表符
			outerExpression = new ScoreExpression(outerExpressionString);

			// 获取评分提供者配置项
			Node scoreProviderNode = (Node) cfg.evaluate("config/"
					+ SCORE_PRIVIDER, XPathConstants.NODE);
			if (scoreProviderNode == null) {
				throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
						"ExpressionXscorerFactory config file must has a element: '"
								+ SCORE_PRIVIDER + "'");
			}

			String scorePrividerName = scoreProviderNode.getTextContent();
			if (scorePrividerName == null) {
				throw new SolrException(
						SolrException.ErrorCode.SERVER_ERROR,
						"the element '"
								+ SCORE_PRIVIDER
								+ "' in ExpressionXscorerFactory config file can't be null");
			}
			scorePrividerName = scorePrividerName.replaceAll("\t|\r|\n", "");
			// 去除字符串中的回车/换行符/制表符
			scoreProvider = (ScoreProvider) SpringContextHolder.getInstance()
					.getContext().getBean(scorePrividerName);
			if (scoreProvider == null) {
				throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
						"Not found bean :'" + scorePrividerName
								+ "' in Spring Application Context.");
			}
			statusMessage = "XscorerFactory's configuration has been loaded.";
		} catch (Exception e) {
			throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
					"Error load ExpressionXscorerFactory's configuration.", e);
		} finally {
			importLock.unlock();
		}

	}

	@Override
	public void reConfigure() {
		loadConfig();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.solr.schema.XscorerFactory#reloadData()
	 */
	@Override
	public void refresh() {
		loadData();
	}

	private void loadData() {
		if (!importLock.tryLock()) {
			log.warn("ExpressionXscorerFactory is running.");
			return;
		}
		try {
			statusMessage = "Loading xscore data...";
			scoreProvider.load();
			statusMessage = "Xscore data has been loaded.";
		} finally {
			importLock.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.solr.schema.XscorerFactory#getXscorer()
	 */
	@Override
	public Xscorer getXscorer() {
		// return scatterFieldName == null ? new ExpressionXscorer(schema, this,
		// expression) : new ExpressionXscorer(schema, this, expression,
		// scatterFieldName);
		return new ExpressionXscorer(schema, this);
	}

	/**
	 * @return the innerExpression
	 */
	public ScoreExpression getInnerExpression() {
		return innerExpression;
	}

	/**
	 * @return the outerExpression
	 */
	public ScoreExpression getOuterExpression() {
		return outerExpression;
	}

	public float getOuterScore(Long id) {
		return scoreProvider.getScore(id);
	}
}
